#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
协议映射工具
用于根据协议ID获取对应的Python消息类
"""

import inspect
from typing import Dict, Type, Optional
from google.protobuf.message import Message

try:
    from Script.protobuf import protocol_pb2
except ImportError:
    import protocol_pb2


class ProtocolMapper:
    """协议映射器，用于协议ID和消息类之间的映射"""
    
    def __init__(self):
        self._id_to_class_map: Dict[int, Type[Message]] = {}
        self._class_to_id_map: Dict[Type[Message], int] = {}
        self._initialize_mappings()
    
    def _initialize_mappings(self):
        """初始化协议ID到消息类的映射关系"""
        # 获取所有消息类
        message_classes = self._get_all_message_classes()
        
        # 建立映射关系
        for message_class in message_classes:
            protocol_id = self._get_protocol_id_from_class(message_class)
            if protocol_id is not None:
                self._id_to_class_map[protocol_id] = message_class
                self._class_to_id_map[message_class] = protocol_id
    
    def _get_all_message_classes(self) -> list:
        """获取protocol_pb2模块中所有的消息类"""
        message_classes = []
        
        # 遍历protocol_pb2模块中的所有属性
        for name in dir(protocol_pb2):
            obj = getattr(protocol_pb2, name)
            
            # 检查是否为消息类（继承自Message且不是抽象类）
            if (inspect.isclass(obj) and 
                issubclass(obj, Message) and 
                obj != Message and
                not name.startswith('_')):
                message_classes.append(obj)
        
        return message_classes
    
    def _get_protocol_id_from_class(self, message_class: Type[Message]) -> Optional[int]:
        """从消息类中获取协议ID"""
        try:
            # 创建消息实例
            instance = message_class()
            
            # 检查是否有MessageID字段
            if hasattr(instance, 'MessageID'):
                # 获取默认的MessageID值
                field_descriptor = instance.DESCRIPTOR.fields_by_name.get('MessageID')
                if field_descriptor and field_descriptor.has_default_value:
                    return field_descriptor.default_value
                
                # 如果没有默认值，尝试获取当前值
                message_id = getattr(instance, 'MessageID', None)
                if message_id is not None:
                    return int(message_id)
            
            # 尝试通过类名匹配EMessageID枚举
            class_name = message_class.__name__
            return self._get_protocol_id_by_name(class_name)
            
        except Exception as e:
            print(f"获取协议ID失败 - 类: {message_class.__name__}, 错误: {e}")
            return None
    
    def _get_protocol_id_by_name(self, class_name: str) -> Optional[int]:
        """通过类名匹配EMessageID枚举值"""
        try:
            # 将类名转换为枚举名格式（如NetConnect -> NET_CONNECT）
            enum_name = self._class_name_to_enum_name(class_name)
            
            # 检查EMessageID枚举中是否存在对应的值
            if hasattr(protocol_pb2.EMessageID, enum_name):
                return getattr(protocol_pb2.EMessageID, enum_name)
            
            return None
        except Exception:
            return None
    
    def _class_name_to_enum_name(self, class_name: str) -> str:
        """将类名转换为枚举名格式
        例如: NetConnect -> NET_CONNECT
        """
        # 在大写字母前插入下划线，然后转为大写
        import re
        enum_name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', class_name)
        return enum_name.upper()
    
    def get_message_class_by_id(self, protocol_id: int) -> Optional[Type[Message]]:
        """根据协议ID获取对应的消息类
        
        Args:
            protocol_id: 协议ID，如100对应NET_CONNECT
            
        Returns:
            对应的消息类，如果找不到则返回None
        """
        return self._id_to_class_map.get(protocol_id)
    
    def get_protocol_id_by_class(self, message_class: Type[Message]) -> Optional[int]:
        """根据消息类获取对应的协议ID
        
        Args:
            message_class: 消息类
            
        Returns:
            对应的协议ID，如果找不到则返回None
        """
        return self._class_to_id_map.get(message_class)
    
    def get_all_mappings(self) -> Dict[int, Type[Message]]:
        """获取所有协议ID到消息类的映射
        
        Returns:
            协议ID到消息类的字典映射
        """
        return self._id_to_class_map.copy()
    
    def print_all_mappings(self):
        """打印所有映射关系，用于调试"""
        print("协议ID到消息类的映射关系:")
        for protocol_id, message_class in sorted(self._id_to_class_map.items()):
            print(f"  {protocol_id}: {message_class.__name__}")


# 创建全局实例
_protocol_mapper = ProtocolMapper()


def get_message_class_by_id(protocol_id: int) -> Optional[Type[Message]]:
    """根据协议ID获取对应的消息类（全局函数）
    
    Args:
        protocol_id: 协议ID，如100对应NET_CONNECT
        
    Returns:
        对应的消息类，如果找不到则返回None
        
    Example:
        >>> message_class = get_message_class_by_id(100)
        >>> if message_class:
        >>>     instance = message_class()
        >>>     print(f"消息类: {message_class.__name__}")
    """
    return _protocol_mapper.get_message_class_by_id(protocol_id)


def get_protocol_id_by_class(message_class: Type[Message]) -> Optional[int]:
    """根据消息类获取对应的协议ID（全局函数）
    
    Args:
        message_class: 消息类
        
    Returns:
        对应的协议ID，如果找不到则返回None
    """
    return _protocol_mapper.get_protocol_id_by_class(message_class)


def get_all_protocol_mappings() -> Dict[int, Type[Message]]:
    """获取所有协议映射（全局函数）
    
    Returns:
        协议ID到消息类的字典映射
    """
    return _protocol_mapper.get_all_mappings()


def print_all_protocol_mappings():
    """打印所有协议映射关系（全局函数）"""
    _protocol_mapper.print_all_mappings()


if __name__ == "__main__":
    # 测试代码
    print("协议映射工具测试")
    print("=" * 50)
    
    # 测试根据协议ID获取消息类
    test_ids = [100, 101, 102]  # NET_CONNECT, NET_DISCONNECT等
    
    for protocol_id in test_ids:
        message_class = get_message_class_by_id(protocol_id)
        if message_class:
            print(f"协议ID {protocol_id} -> 消息类: {message_class.__name__}")
            
            # 创建实例测试
            try:
                instance = message_class()
                print(f"  成功创建实例: {type(instance).__name__}")
            except Exception as e:
                print(f"  创建实例失败: {e}")
        else:
            print(f"协议ID {protocol_id} -> 未找到对应的消息类")
    
    print("\n" + "=" * 50)
    print("所有协议映射关系:")
    print_all_protocol_mappings()